package club.kingyin.kyblogprogateway.filter;

import club.kingyin.commons.clients.UserClient;
import club.kingyin.commons.constant.AuthorizeConstant;
import club.kingyin.commons.constant.LogConstant;
import club.kingyin.commons.entity.user.Log;
import club.kingyin.commons.response.Result;
import club.kingyin.kyblogprogateway.core.Distributed;
import club.kingyin.kyblogprogateway.core.PathFilters;
import club.kingyin.pathfilter.core.PathFilter;
import com.alibaba.fastjson.JSON;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;

// 所有请求都加上日志(异步操作)
@Order(-1)
@Component
public class LogFilter implements GlobalFilter {

    @Autowired
    private Distributed distributed;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String url = request.getPath().value();
        if (!PathFilters.LOG_PATH.intercept(url)) {
            return chain.filter(exchange);
        }
        // 分布式id
        Integer id = distributed.nextId(LogConstant.LOG_ID);
        String key = String.valueOf(id);
        MultiValueMap<String, String> params = request.getQueryParams();
        // 加入日志id
        ServerHttpRequest logHeader = request.mutate().header(LogConstant.LOG_ID, key).build();
        HttpHeaders headers = request.getHeaders();
        Log log = new Log();
        if (headers.containsKey(AuthorizeConstant.ID)) {
            log.setUserid(Integer.parseInt(Objects.requireNonNull(headers.getFirst(AuthorizeConstant.ID))));
        }
        log.setHeader(headers);
        log.setParams(params);
        log.setTimestamp(System.currentTimeMillis());
        log.setUrl(url);
        log.setId(id);
        String method = request.getMethodValue();
        if (HttpMethod.POST.matches(method)) {
            return DataBufferUtils.join(exchange.getRequest().getBody())
                    .flatMap(dataBuffer -> {
                        byte[] bytes = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(bytes);
                        String bodyString = new String(bytes, StandardCharsets.UTF_8);
                        if ("/file/bucket/upload".equals(request.getPath().value())) {
                            log.setParams("文件");
                        } else {
                            log.setParams(JSON.parseObject(bodyString));
                        }
                        log.setResponse(Result.success(null,"logging"));
                        // 发送保存日志消息
                        rabbitTemplate.convertAndSend("log.direct","save",log);
                        exchange.getAttributes().put("POST_BODY", bodyString);
                        DataBufferUtils.release(dataBuffer);
                        Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                            DataBuffer buffer = exchange.getResponse().bufferFactory()
                                    .wrap(bytes);
                            return Mono.just(buffer);
                        });

                        ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
                                exchange.getRequest()) {
                            @Override
                            public Flux<DataBuffer> getBody() {
                                return cachedFlux;
                            }
                        };
                        return chain.filter(exchange.mutate().request(mutatedRequest)
                                .build());
                    });
        } else if (HttpMethod.GET.matches(method)) {
            Map m = request.getQueryParams();
            if (!m.isEmpty()) {
                log.setParams(JSON.toJSONString(m));
            }
            log.setResponse(Result.success(null,"logging"));
            // 发送保存日志消息
            rabbitTemplate.convertAndSend("log.direct","save",log);
        }
        return chain.filter(exchange.mutate().request(logHeader).build());
    }

}
